/*
 * @Date: 2022-04-17 23:44:37
 * @LastEditTime: 2022-05-16 10:46:57
 * @brief chatroom client code
 */
#include "messageClass.hpp"
#include "userClass.hpp"
#include "winUDPsocket.hpp"
#include "winTCPsocket.hpp"
#include "env.h"

#include <conio.h>
#include <string.h>
#include <Windows.h>

winUDPSocket sock;
Message Mes;
userInfo User;

enum userStatus {
  NOTLOGIN = 0,
  LOGINED = 1,
  CHATTING = 2
};

int rw_mutex = 0;

userStatus status = NOTLOGIN;

char resvBuffer[max_message_length] = {0}; // 接收缓冲区
char zero[1] = {0};
char sendBuffer[MAX_BODY_LENGTH] = {0}; // 输入缓冲区
int point = 0; // 输入缓冲区的指针

int sendMes(MessageType type, char *source, char *destination, const void* buffer, std::size_t bufferSize) {
  Mes.setMessage(type, source, destination, buffer, bufferSize);
  sock.Send(Mes.data(), max_message_length);
  return 1;
}

int Resv() {
  sock.Resv(resvBuffer, max_message_length);
  Mes.set_data(resvBuffer);
  return 1;
}

int login() {
  User = inputUserInfo();
  std::cout << User.username << std::endl;
  sendMes(MT_USERINFO, User.username, (char *)&SERVER_NAME, (char *)&User, sizeof(userInfo));
  Resv();
  if (Mes.head().status == SUCCESS) {
    status = LOGINED;
    return 1;
  } else if (Mes.head().status == PASSWORDERROR) {
    cout << "Password error! Please check and try again!\n" << endl;
  } else if (Mes.head().status == NOTFOUND) {
    cout << "Username is not exited! Please check and try again!\n" << endl;
  }
  return 0;
}

DWORD WINAPI listen(LPVOID pM) {
  while(status == CHATTING) {
    Resv();
    if (Mes.head().type == MT_CHATINFO && Mes.head().status == SUCCESS) {
      rw_mutex = 1;
      for (int i = 0; i < point + strlen(User.username) + 2; i++) {
        cout << "\b \b";
      }
      cout << Mes.head().source << ">>" << Mes.body() << endl;
      // if (Mes.head().source == User.username) {
      //   rw_mutex = 1;
      //   memset(sendBuffer, 0, point*sizeof(char));
      //   point = 0;
      //   cout << endl;
      //   rw_mutex = 0;
      // }
      // cout << point << endl;
      cout << User.username << ">>";
      cout << sendBuffer;
      rw_mutex = 0;
    }
  }
  return 1;
}

// 接收到消息时需要用退格清除当前行输出并且重新换行
int chat() {
  status = CHATTING;
  system("cls");
  cout << "please input chat content, enter send, ESC exit" << endl;
  HANDLE hThread;
  DWORD threadId;
  hThread = CreateThread(NULL, 0, listen, 0, 0, &threadId);
  char c;
  cout << User.username << ">>";
  while(1) {
    while (rw_mutex == 1);
    if (!_kbhit()) continue;
    c = _getch();
    if (c == 27) {
      status = LOGINED;
      sendMes(EXIT, User.username, (char *)&SERVER_NAME, (char *)&User, sizeof(userInfo));
      return 1; // 遇到esc退出
    }
    else if (c == 13) {
      if (point == 0) {continue;}
      // cout << sendBuffer << endl;
      sendMes(MT_CHATINFO, User.username, (char *)&SERVER_NAME, sendBuffer, point);
      for (int i = 0; i < point + strlen(User.username) + 2; i++) {
        cout << "\b \b";
      }
      memset(sendBuffer, 0, point * sizeof(char));
      point = 0;
      // cout << Mes.body() << endl;
      // Resv();
      // if (Mes.head().Status == SUCCESS) {
      //   memset(sendBuffer, 0, point*sizeof(char));
      //   point = 0;
      //   cout << endl;
      //   cout << User.username << ">>"; 
      // }
      // else 
      // {
      //   for (int i = 0; i < point + strlen(User.username) + 2; i++) {
      //     cout << "\b \b";
      //   }
      //   cout << "Send error!" << endl;
      //   cout << User.username << ">>";
      //   cout << sendBuffer;
      // }
    }
    else if (c == 8) { // 退格符
      if(point == 0) { continue; }
      sendBuffer[--point] = 0;
      cout << "\b \b";
    }
    else {
      if(point >= MAX_BODY_LENGTH - 1) {
        for (int i = 0; i < point + strlen(User.username) + 2; i++) {
          cout << "\b \b";
        }
        cout << "already max length message!" << endl;
        cout << User.username << ">>";
        cout << sendBuffer;
        continue;
      }
      cout << c ;
      sendBuffer[point++] = c;
    }
  }
  WaitForSingleObject(hThread, INFINITE);
  return 0;
}

int upld(){
    //先输入上传的文件名
    char filename[100] = {0};  //文件名
    printf("Input filename to save: ");
    gets(filename);
    FILE *fp = fopen(filename, "rb");  //以二进制方式打开（创建）文件
    if(fp == NULL){
        cout << "file not exist, press any key to back main." << endl;
        char c = _getch();
        return 1;
    }
    sendMes(MT_FILE_UPLOAD, User.username, (char *)&SERVER_NAME, filename, 20);
    Resv();
    if(Mes.head().status != SUCCESS) {
      cout << "file not exist, press any key to back main." << endl;
      char c = _getch();
      return 1;
    }
    WSADATA wsaData;
    WSAStartup(MAKEWORD(2, 2), &wsaData);
    SOCKET sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
    sockaddr_in sockAddr;
    memset(&sockAddr, 0, sizeof(sockAddr));  //每个字节都用0填充
    sockAddr.sin_family = PF_INET; // IPv4 地址
    sockAddr.sin_addr.s_addr = inet_addr(SERVER_IP); // IPv4 地址
    sockAddr.sin_port = htons(SERVER_PORT); // 端口
    connect(sock, (SOCKADDR*)&sockAddr, sizeof(SOCKADDR));
    //循环接收数据，直到文件传输完毕
    char buffer[512] = {0};  //文件缓冲区
    int nCount;
    // while( (nCount = recv(sock, buffer, 512, 0)) > 0 ){
    //     fwrite(buffer, nCount, 1, fp);
    // }
    while( (nCount = fread(buffer, 1, 512, fp)) > 0 ){
      send(sock, buffer, nCount, 0);
    }
    puts("File transfer success!");
    //文件接收完毕后直接关闭套接字，无需调用shutdown()
    fclose(fp);
    closesocket(sock);
    WSACleanup();
    return 0;
}

int downld() {
  char filename[20] = {0};  //文件名
  printf("Input filename to download: ");
  gets(filename);
  sendMes(MT_FILE_DOWNLOAD, User.username, (char *)&SERVER_NAME, filename, 20);
  Resv();
  if(Mes.head().status == NOTFOUND) {
    cout << "file not exist, press any key to back main." << endl;
    char c = _getch();
    return 1;
  }
  winTCPSocket fileSock;
  FILE *fp = fopen(filename, "wb");  //以二进制方式打开（创建）文件
  if(fp == NULL){
      printf("Cannot open file, press any key to exit!\n");
      system("pause");
      exit(0);
  }
  
  fileSock.Connect(); 
  //循环接收数据，直到文件传输完毕
  char buffer[512] = {0};  //文件缓冲区
  int nCount;
  while( (nCount = fileSock.Resv(buffer, 512)) > 0 ){
      fwrite(buffer, nCount, 1, fp);
  }
  puts("File transfer success!");
  //文件接收完毕后直接关闭套接字，无需调用shutdown()
  fclose(fp);
  return 1;
}


int main() {
  char c;
  while(status == NOTLOGIN) {
    login();
  }
  while(1){
    system("cls");
    cout << "click 1 to chat, click 2 to upload, click 3 to download, esc to exit" << endl;
    c = _getch();
    switch (c) {
      case 27:
        return 0;
      case 49:
        chat();
        break;
      case 50:
        upld();
        break;
      case 51:
        downld();
        break;
      default:
        cout << "error input!" << endl;
    }
  }
  return 1;
}